home *** CD-ROM | disk | FTP | other *** search
- 25-Minute Workout
- Part V Answers
- 1
- a. The solution I chose was to give Savings its old display() function back. To do this, I
- made the following additions (things that remained the same are not shown):
- class Account
- {
- //everything else the same as before
- virtual void display( ); //make this a virtual function
- };
-
- //display - generic display function
- // (virtual fns canÆt be inline)
- void Account::display( )
- {
- cout << ôAccount ô << accountNumber
- << ô = ô << balance
- << ô\nö;
- }
-
- //Savings class with addition of display function
- class Savings : public Account
- {
- public:
- //everything else the same here, too
- virtual void display( ); //add this declaration
- };
-
- //display - unique display function for Savings
- void Savings::display( )
- {
- cout << ôAccount ô << accountNumber
- << ô = ô << balance
- << ô (no. withdrawals = ô << noWithdrawals
- << ô)\nö;
- }
- I declared Account::display() to be virtual so that I could overload it in the subclass
- Savings. I then added the Savings::display() function from BUDGET4 to Savings.
- Now, when main() displays the different accounts, the proper display() function is
- chosen automatically, depending on the account type. The output of savings accounts
- now appears with information on the number of withdrawals, as before:
- Account totals:
- Account 123 = 95 (no. withdrawals = 2)
- Account 234 = 154.600006
- Total worth = 249.600006
- b. My class, NewSavings, appears as follows:
- //NewSavings - implement a new savings account that charges
- // no withdrawal fee if balance is >= 750
- class NewSavings : public Savings
- {
- public:
- NewSavings(unsigned accNo, float initialBalance = 0.0) :
- Savings(accNo, initialBalance) {}
- //transaction functions++
- virtual void withdrawal(float amount);
- };
-
- void NewSavings::withdrawal(float amount)
-
- {
- if (balance < amount)
- {
- cout << ôInsufficient funds: balance ô << balance
- << ô, withdrawal ô << amount
- << ô\nö;
- }
- else
- {
- //if the balance is less than 750...
- if (balance < 750.00F)
- {
- //...treat just like old savings account;
- Savings::withdrawal(amount); //Note 1
- }
- //...otherwise,...
- else
- {
- //...no service charge (Yeah!)
- balance -= amount;
- }
- }
- }
- Here, you see that once again the member function withdrawal() is overloaded by
- one, which has the desired effect. Notice, however, that if the balance is below $750,
- this new withdrawal() calls the old Savings::withdrawal() (Note 1). Remember that
- including the name of the class in the call forces it to bind early even if the function is
- declared virtual.
- c. Start with the separate list approach. This program does not differ much from the
- BUDGET5 program presented earlier, but the changes are scattered over much of the
- program. Therefore, to avoid confusion, IÆve repeated the entire program here:
- //BUDGET5.1c - this version splits the linked list into two
- // by establishing two different pFirst pointers,
- // one to the first Checking account object and
- // the other to the first Savings account object.
- //
- // This is the solution to problem 1c.
- //
-
- #include <iostream.h>
- #include <stdlib.h>
- #include <ctype.h>
- //Account - this abstract class incorporates properties
- // common to both account types Checking and
- // Savings; however, itÆs missing the concept
- // withdrawal( ) which is different between the two
- class Account
- {
- protected:
- Account(Account &c)
- {
- cout << ôNo creating funds\nö;
- }
-
- //this function adds an object to the list pointed
- //at by the argument provided to the function
- void addToList(Account * &pFirst); //Note 1
-
- public:
- Account(unsigned accNo, float initialBalance = 0.0F);
-
- //access functions
- int accountNo( )
- {
- return accountNumber;
- }
- float acntBalance( )
- {
- return balance;
- }
-
- static int noAccounts( )
- {
- return count;
- }
-
- //transaction functions
- void deposit(float amount)
- {
- balance += amount;
- }
- virtual void withdrawal(float amount) = 0;
-
- //display function for displaying self on æcoutÆ
- void display( )
- {
- cout << ôAccount ô << accountNumber
- << ô = ô << balance
- << ô\nö;
- }
-
- //make the following functions pure virtual
- Account *next( )
- {
- return pNext;
- }
-
-
- protected:
- static int count; //number of accounts
- unsigned accountNumber;
- float balance;
-
- //pNext is still in Account but pFirst //Note 2
- //has now been relegated to the subclasses
- Account *pNext;
- };
- int Account::count = 0;
-
- Account::Account(unsigned accNo, float initialBalance)
- {
- accountNumber = accNo;
- balance = initialBalance;
- count++;
- }
-
- //addToList - by accepting a reference to pFirst
- // as an argument, addToList( ) can be called
- // from either Checking or Savings
- void Account::addToList(Account * &pFirst)
- {
- //this comes out of the constructor for Account
- if (pFirst == 0)
- {
- pFirst = this; //empty list; make it first
- }
- else { //list not empty; look for last...
- //...entry in the list
- for (Account *pA = pFirst; pA->pNext; pA = pA->pNext)
- {
- }
- pA->pNext = this; //tack us onto end
- }
- pNext = 0; //weÆre always last
- }
-
- //Checking - this class contains properties unique to
- // checking accounts. Not much left is there?
- class Checking : public Account
- {
- public:
- //here the constructor defined inline
- Checking(unsigned accNo, float initialBalance = 0.0F) :
- Account(accNo, initialBalance)
- {
- addToList(Checking::pFirst); //Note 3
- }
-
- //overload pure virtual functions
- virtual void withdrawal(float amount);
-
- //return first object in Checking account list
- static Account* first( ) //Note 4
- {
- return (Account*)Checking::pFirst;
- }
- protected:
- static Account* pFirst;
- };
- Account *Checking::pFirst = 0;
-
- void Checking::withdrawal(float amount)
- {
- if (balance < amount)
- {
- cout << ôInsufficient funds: balance ô << balance
- << ô, check ô << amount
- << ô\nö;
- }
- else
- {
- balance -= amount;
-
- //if balance falls too low, charge service fee
- if (balance < 500.00F)
- {
- balance -= 0.20F;
- }
- }
- }
-
-
- //Savings - same story as Checking except that it also
- // has a unique data member
- class Savings : public Account
- {
- public:
- //here the constructor is defined as a separate
- //function just to show you the difference
- Savings(unsigned accNo, float initialBalance = 0.0F) :
- Account(accNo, initialBalance)
- {
- noWithdrawals = 0;
- addToList(Savings::pFirst);
- }
-
- //transaction functions
- virtual void withdrawal(float amount);
- static Account* first( )
- {
- return (Account*)Savings::pFirst;
- }
-
-
- protected:
- int noWithdrawals;
- static Account *pFirst;
- };
- Account* Savings::pFirst = 0;
-
- void Savings::withdrawal(float amount)
- {
- if (balance < amount)
- {
- cout << ôInsufficient funds: balance ô << balance
- << ô, withdrawal ô << amount
- << ô\nö;
- }
- else
- {
- if (++noWithdrawals > 1)
- {
- balance -= 5.00F;
- }
- balance -= amount;
- }
- }
-
- //prototype declarations
- unsigned getAccntNo( );
- void process(Account &account);
- void outOfMemory( );
- int main( );
-
- //main - accumulate the initial input and output totals
- int main( )
- {
- /*loop until someone enters an æXÆ or æxÆ*/
- Account *pA;
- char accountType; //S or C
-
- unsigned keepLooping = 1;
- while (keepLooping)
- {
- cout << ôEnter S for Savings, ô
- ôC for Checking, X for exit\nö;
- cin >> accountType;
-
- switch (accountType)
- {
- case æcÆ:
- case æCÆ:
- pA = new Checking(getAccntNo( ));
- if (pA == 0)
- {
- outOfMemory( );
- }
- process(*pA);
- break;
-
- case æsÆ:
- case æSÆ:
- pA = new Savings(getAccntNo( ));
- if (pA == 0)
- {
- outOfMemory( );
- }
- process(*pA);
- break;
- case æxÆ:
- case æXÆ:
- keepLooping = 0;
- break;
-
- default:
- cout << ôI didnÆt get that.\nö;
- }
- }
-
- //now present totals //Note 5
- float subTotal = 0.0F;
- float total = 0.0F;
- cout << ôAccount totals:\nö;
-
- //we are now forced to display the lists separately
- for (pA = Checking::first( ); pA; pA = pA->next( ))
- {
- pA->display( );
- subTotal += pA->acntBalance( );
- }
- cout << ôTotal of all checking accounts = ô << subTotal << ô\nö;
- total += subTotal;
-
- //repeat the process for savings
- subTotal = 0.0F;
- for (pA = Savings::first( ); pA; pA = pA->next( ))
- {
- pA->display( );
- subTotal += pA->acntBalance( );
- }
- cout << ôTotal of all savings accounts = ô << subTotal << ô\nö;
- total += subTotal;
-
- cout << ôTotal worth of all accounts = ô << total << ô\nö;
- return 0;
- }
-
- //getAccntNo - return the account number entered
- unsigned getAccntNo( )
- {
- unsigned accntNo;
- cout << ôEnter account number:ö;
- cin >> accntNo;
- return accntNo;
- }
-
- //process(Account) - input the data for an account*/
- void process(Account &account)
- {
- cout << ôEnter positive number for deposit,\nö
- ônegative for withdrawal, 0 to terminateö;
-
- float transaction;
- do
- {
- cout << ô:ö;
- cin >> transaction;
-
- //deposit
- if (transaction > 0.0F)
- {
- account.deposit(transaction);
- }
-
- //withdrawal
- if (transaction < 0.0F) {
- account.withdrawal(-transaction);
- }
- } while (transaction != 0.0F);
- }
-
- //outOfMemory - generate out-of-memory message and quit
- void outOfMemory( )
- {
- cout << ôOut of memory\nö;
- abort( );
- }
- The goal is to split the linked list of Account objects into two linked lists, one of
- Checking objects and another of Savings objects. To do so, the static data member
- pFirst has been moved out of the class Account (Note 2) and into the classes
- Checking and Savings (Note 4). Giving each subclass its own pFirst pointer allows
- them to maintain their own separate linked lists. This change means that the member
- function first() must be moved into the subclasses as well. (The pointer pNext does
- not need to be moved because both types of accounts include pointers to the next
- member of their respective list.)
- Because pFirst is no longer a member of Account, the constructor for Account cannot
- refer it. Looked at another way, the constructor for Account cannot add the object to
- one of the lists because it does not know which list to add it to: The object is not yet
- either a Savings account object or a Checking account object.
- The job of adding the object to the linked list must be moved into the constructor for
- Savings and Checking (Note 3). The work of adding an object to the list is the same
- for both classes, so there is not a problem with these classes calling a common
- function and passing the proper pFirst pointer upon which to operate (Note 1). (The
- argument to addToList() is a reference to a pointer so that the function can change the
- value of the pointer passed to it.)
- The remainder of the program is the same as in BUDGET5 until the end of main(),
- where the program displays the lists of accounts. Rather than one loop as before, the
- program must now loop twice: the first time starting with the object returned from
- Checking::first(), that is, the first checking account, and a second time starting with
- the object returned by Savings::first(), that is, the first savings account (Note 5).
- The output from this program appears as follows:
- Enter S for Savings, C for Checking, X for exit
- S
- Enter account number:1234
- Enter positive number for deposit,
- negative for withdrawal, 0 to terminate:100
- :0
- Enter S for Savings, C for Checking, X for exit
- C
- Enter account number:2345
- Enter positive number for deposit,
- negative for withdrawal, 0 to terminate:200
- :0
- Enter S for Savings, C for Checking, X for exit
- S
- Enter account number:3456
- Enter positive number for deposit,
- negative for withdrawal, 0 to terminate:300
- :0
- Enter S for Savings, C for Checking, X for exit
- C
- Enter account number:4567
- Enter positive number for deposit,
- negative for withdrawal, 0 to terminate:400
- :0
- Enter S for Savings, C for Checking, X for exit
- X
- Account totals:
- Account 2345 = 200
- Account 4567 = 400
- Total of all checking accounts = 600
- Account 1234 = 100
- Account 3456 = 300
- Total of all savings accounts = 400
- Total worth of all accounts = 1000
- A second approach is to leave the objects mixed in the same linked list and divide
- them during the display process. This requires the addition of a run-time function (as
- I explain in Chapter 19), called runtimeType() here. The return from runtimeType() is
- used to discriminate the objects. The class additions are shown in Chapter 19, so only
- the relevant section of main() appears here:
- //now present totals
- float cTotal = 0.0;
- float sTotal = 0.0;
- cout << ôAccount totals:\nö;
- for (pA = Account::first( ); pA; pA = pA->next( ))
- {
- pA->display( );
- //keep checking and saving account totals separate
- switch(pA->runtimeType( ))
- {
- case Account::CHECKING:
- cTotal += pA->acntBalance( );
- break;
- case Account::SAVINGS:
- sTotal += pA->acntBalance( );
- break;
- default:
- cout << ôUnknown account type encountered\nö;
- }
- }
- cout << ôChecking account total = ô << cTotal << ô\nö;
- cout << ôSavings account total = ô << sTotal << ô\nö;
- cout << ôTotal worth = ô << (cTotal+sTotal) << ô\nö;
- The switch statement keeps the checking and savings account totals in separate piles.
- Neither approach is completely extensible because both must make explicit reference
- to the particular subclasses of Account. However, the first approach is the more
- flexible of the two.
- 2
- This is one of those great questions because there is no wrong answer. My answer follows, but yours is
- probably just as right (or more so) even if it differs from mine. See CLASS.PCX on the disk for my
- answer. The concrete classes are PanelTruck, 18Wheeler, Pickup, Sedan, StationWagon, and CV2. The rest
- are abstract classes.
- I started by dividing vehicles into gasoline- and diesel-powered varieties. The two types of trucks provided
- were both diesel (I assumed), but they differed in that one has a detachable trailer and the other does not.
- The gasoline vehicles were divided along the lines of water cooled and air cooled. Water-cooled vehicles
- were divided into regular cars and cowboy Cadillacs (pickups). Many station wagons are more similar to
- pickups than sedans, so I threw them into the pickup category.
- ItÆs good practice to consciously build these types of class hierarchies. So, the next time youÆre lying back
- in the tub or caught in traffic, imagine adding other types of vehicles in this framework.
-
-
-
-